#ifndef _B5M_COMMON_MUTEX_H_
#define _B5M_COMMON_MUTEX_H_

#include "common.h"

B5M_BEGIN_VERSIONED_NAMESPACE_DECL

#define USYNC_THREAD            0
#define USYNC_PROCESS           1 

typedef struct
{
	/// Either USYNC_THREAD or USYNC_PROCESS
	int type_;
	union
	{
		HANDLE proc_mutex_;
		CRITICAL_SECTION thr_mutex_;
	};
} B5M_mutex_t;


/**
 * @class B5M_Mutex
 *
 * @brief @c B5M_Mutex wrapper (valid in same process or across
 *        processes (depending on @a TYPE flag)).  In general,
 *        however, we recommend using @a B5M_Process_Mutex or @a
 *        B5M_Thread_Mutex rather than @a B5M_Mutex.
 */
class B5M_Export B5M_Mutex
{
public:
  /// Initialize the mutex.
  B5M_Mutex (int type = USYNC_THREAD,const wchar_t *name = 0);

  /// Implicitly destroy the mutex.
  ~B5M_Mutex (void);

  /// Explicitly destroy the mutex.
  /**
   * @note Only one thread should call this method since it doesn't
   *        protect against race conditions.
   */
  int remove (void);

  /// Acquire lock ownership (wait on queue if necessary).
  int acquire (void);

  /// Block the thread until the mutex is acquired or @a tv times out,
  /// in which case -1 is returned and @c errno == @c ETIME.
  /**
   * @note @a timeout is assumed  to be in "absolute" rather than
   * "     relative" time.  The value of @a tv is updated upon return
   *       to show the actual(absolute) acquisition time.
   */
  int acquire (B5M_UINT32 timeout);

  /// Conditionally acquire lock (i.e., don't wait on queue).
  /**
   * @return -1 on failure.  If we "failed" because someone
   *         else already had the lock, @c errno is set to @c EBUSY.
   */
  int tryacquire (void);

  /// Release lock and unblock a thread at head of queue.
  int release (void);

public:
  /**
   * Remember the name of the mutex if we created it so we can unlink
   * it when we go away (only the actor that initialized the memory
   * can destroy it).
   */
  const wchar_t *lockname_;

  /// Mutex type supported by the OS.
  B5M_mutex_t lock_;

  /// Keeps track of whether @c remove has been called yet to avoid
  /// multiple @c remove calls, e.g., explicitly and implicitly in the
  /// destructor.  This flag isn't protected by a lock, so make sure
  /// that you don't have multiple threads simultaneously calling
  /// @c remove on the same object, which is a bad idea anyway.
  bool removed_;

private:
  // Prevent assignment and initialization.
	B5M_NONE_COPYABLE(B5M_Mutex);
};

B5M_END_VERSIONED_NAMESPACE_DECL

#endif